﻿using agsXMPP;
using agsXMPP.protocol.client;
using agsXMPP.protocol.iq.roster;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Threading;

namespace JClient.Contacts {
	public class RosterContactCollection : ObservableCollection<RosterContact> {
		private Dispatcher Dispatcher;
		private bool serverOnly;

		public RosterContactCollection(XmppClientConnection Client) {
			this.Dispatcher = Dispatcher.CurrentDispatcher;
			Client.OnRosterStart += new ObjectHandler(Client_OnRosterStart);
			Client.OnRosterItem += new XmppClientConnection.RosterHandler(Client_OnRosterItem);
			Client.OnRosterEnd += new ObjectHandler(Client_OnRosterEnd);
			Client.OnPresence += new PresenceHandler(Client_OnPresence);
		}
		public RosterContactCollection(XmppClientConnection Client, bool WithoutUsername) {
			this.Dispatcher = Dispatcher.CurrentDispatcher;
			this.serverOnly = WithoutUsername;
			Client.OnRosterStart += new ObjectHandler(Client_OnRosterStart);
			Client.OnRosterItem += new XmppClientConnection.RosterHandler(Client_OnRosterItem);
			Client.OnRosterEnd += new ObjectHandler(Client_OnRosterEnd);
			Client.OnPresence += new PresenceHandler(Client_OnPresence);
		}
		private void Client_OnRosterStart(object sender) {
			if (!this.Dispatcher.CheckAccess( )) {
				this.Dispatcher.Invoke(new ObjectHandler(Client_OnRosterStart), sender);
			} else {
				this.Clear( );
			}
		}
		private void Client_OnRosterItem(object sender, RosterItem item) {
			if (!this.Dispatcher.CheckAccess( )) {
				this.Dispatcher.Invoke(new XmppClientConnection.RosterHandler(Client_OnRosterItem), sender, item);
			} else {
				if (serverOnly == (item.Jid.User == null || item.Jid.User == "")) {
					if (this.IndexOfJid(item.Jid) == -1)
						this.Add(new RosterContact(item));
					else
						if (item.Subscription == SubscriptionType.remove)
							this.RemoveByJid(item.Jid);
						else
							this.updateByRoster(item);
				}
			}
		}
		private void Client_OnRosterEnd(object sender) {
			//throw new NotImplementedException( );
		}

		private void Client_OnPresence(object sender, Presence pres) {
			if (!this.Dispatcher.CheckAccess( )) {
				this.Dispatcher.Invoke(new PresenceHandler(Client_OnPresence), sender, pres);
			} else {
				if (pres.From.User != pres.To.User || pres.From.Server != pres.To.Server) {
					if (serverOnly == (pres.From.User == null || pres.From.User == "")) {
						switch (pres.Type) {
							case PresenceType.error: // Błąd!
								// ???
								break;
							case PresenceType.probe:
								// ???
								break;
							case PresenceType.subscribe: // Żąda informacji o naszym statusie
								//Send (subscribed /AND subscribe) OR unsubscribed
								break;
							case PresenceType.unsubscribe: // Nie chce informacji o naszym statusie
								//NONE OR Send unsubscribe
								break;
							case PresenceType.subscribed: // Pozwala na informacje o swoim statusie
								//NONE OR Send subscribed
								break;
							case PresenceType.unsubscribed: // Nie pozwala na informacje o swoim statusie
								//NONE OR Send unsubscribed - 
								break;
							default:
								this.updateByStatus(pres);
								break;
						}
					}
				}
			}
		}

		public void addByRoster(RosterItem contact_info) {
			if (this.IndexOfJid(contact_info.Jid) == -1) {
				this.Add(new RosterContact(contact_info));
			}
		}
		public void updateByRoster(RosterItem contact_info) {
			if (this.IndexOfJid(contact_info.Jid) == -1)
				addByRoster(contact_info);
			else {
				int i = this.IndexOfJid(contact_info.Jid);
				if (i >= 0) {
					if (this[i].Nickname != contact_info.Name)
						this[i].Nickname = contact_info.Name;
					if (this[i].SubscriptionType != contact_info.Subscription)
						this[i].SubscriptionType = contact_info.Subscription;
					if (this[i].Groups != contact_info.GetGroups( ))
						this[i].Groups = contact_info.GetGroups( );
				}
			}
		}
		public void updateByStatus(Presence pres) {
			if (this.IndexOfJid(pres.From) == -1)
				this.AddJid(pres.From);
			int Index = this.IndexOfJid(pres.From);
			if (Index >= 0) {
				this.FindByJid(pres.From).updateByPresence(pres);
			}
		}


		#region Main functions
		public void AddJid(Jid jid) {
			if (IndexOfJid(jid) == -1) {
				this.Add(new RosterContact( ) {
					Jid = jid
				});
			}
		}
		public string[ ] GetJids( ) {
			string[ ] temp = new string[this.Count];
			int i = 0;
			if (this.Count != 0)
				foreach (RosterContact RC in this) {
					RC.Jid.Resource = null;
					temp[i] = RC.Jid.ToString( );
					i++;
				}
			return temp;
		}
		public int IndexOfJid(Jid jid) {
			return this.IndexOf(this.FindByJid(jid));
		}
		public RosterContact FindByJid(Jid jid) {
			var items = from item in this
						where item.Jid.User == jid.User
							&& item.Jid.Server == jid.Server
						select item;
			if (items != null && items.Count( ) > 0)
				return items.First( );
			else
				return null;
		}
		public void RemoveByJid(Jid jid) {
			int index;
			while ((index = this.IndexOfJid(jid)) == -1) {
				this.RemoveAt(index);
			}
		}
		#endregion
	}
}